/*
 * @Author: greyqiu greyqiu@163.com
 * @Date: 2025-03-12 17:07:30
 * @LastEditors: greyqiu greyqiu@163.com
 * @LastEditTime: 2025-03-18 11:41:41
 * @Description: 
 * Copyright (c) 2025 by ${greyqiu}, All Rights Reserved. 
 */

#include "mb_rtu_m.h"
#include "mb_transport.h"
#include "mb_reg.h"

MstObj ModBusCom;

#define BUFFER_LEN  512

uint8_t txBuffer[BUFFER_LEN];
uint8_t rxBuffer[BUFFER_LEN];

static uint16_t calCrc16(uint8_t *ptr, uint8_t len)
{
    unsigned char i;
    unsigned int crc=0xFFFF;
    if(len==0)
    len=2;
    while(len--)
    {
        //WDR();
        crc^=*ptr;
        for(i=0; i<8; i++)
        {
            if(crc&1)
            {
                crc>>=1;
                crc^=0xA001;
            }
            else
                crc>>=1;
        }
        ptr++;
    }
    return(crc);
}

static void writeReg16Handle(Reg16 * reg16, uint16_t num)
{
    // 填充数据段
    for(uint8_t i = 0; i < num; i++)
    {
        reg16[i].u16 = ModBusCom.rxBuf[3 + i*2];
        reg16[i].u16 <<= 8;
        reg16[i].u16 |= ModBusCom.rxBuf[4 + i*2];
    }
}

void mbReadHoldReg(uint16_t slvId, uint16_t addr, uint16_t num)
{
    // 填充帧头
    ModBusCom.txBuf[0] = slvId;       // 从机机地址
    ModBusCom.txBuf[1] = 0x03;        // 读保持寄存器
    ModBusCom.txBuf[2] = addr >> 8;   // 地址
    ModBusCom.txBuf[3] = addr & 0xFF;
    ModBusCom.txBuf[4] = num >> 8;    // 寄存器个数
    ModBusCom.txBuf[5] = num & 0xFF;
    // 尾部填充CRC校验码
    uint16_t crc16 = calCrc16(ModBusCom.txBuf, 6);
    ModBusCom.txBuf[6] = crc16 & 0x00ff; // CRCL
    ModBusCom.txBuf[7] = crc16 >> 8; // CRCH

    ModBusCom.readAddr = addr;
    ModBusCom.txNum = 8; // 设置要发送的长度，触发MODBUS发送
}

static uint16_t rspHandle(uint16_t addr)
{
    if(ModBusCom.rxBuf[0] != DSP_ADDR || ModBusCom.rxBuf[1] != 0x03) // 非本机地址，不理会
    {
        return ERRCODE_DEVICEADDR;
    }

    if(ModBusCom.rxNum < 3) // 长度小于最小帧定义，不理会
    {
        return ERRCODE_DATALEN;
    }

    // CRC校验
    uint16_t temp1 = calCrc16(ModBusCom.rxBuf, ModBusCom.rxNum - 2);// 计算CRC
    uint16_t temp2 = ModBusCom.rxBuf[ModBusCom.rxNum - 1];    // 读取帧中的CRC
    temp2 <<= 8;
    temp2 |= ModBusCom.rxBuf[ModBusCom.rxNum - 2];
    if(temp2 != temp1) { // CRC错误，不理会
        return ERRCODE_CRC;
    }

    uint16_t regNum, region, offset;

    // 解析帧头信息
    regNum = ModBusCom.rxBuf[2] >> 1;     // 寄存器数量

    region = addr / 1000;   // 解析读取哪个寄存器页
    offset = addr % 1000;   // 在页中的偏移
    switch (addr) {
        case 0:   // 第0页
            if (regNum <= MSG_SIZE) {
                writeReg16Handle(&dspReg[0], regNum);
            }
            break;
        default:
            return ERRCODE_DATAADDR;
            break;
    }
    return 0;
}

static uint16_t timeoutHandle(uint16_t addr)
{
    switch (addr) {
        case 0:   // 第0页
            for (uint16_t i = 0; i < 8; i++) {
                dspReg[i].u16 = 0;
            }
            break;
        default:
            return ERRCODE_DATAADDR;
            break;
    }
    return 0;
}

// 对接收到的一包数据进行解析
void COM_appInit(void)
{
    COM_objInit(&ModBusCom, rxBuffer, BUFFER_LEN, txBuffer, BUFFER_LEN, rspHandle, timeoutHandle);
}

void COM_appTskRun()
{
    COM_txProcess(&ModBusCom);
}

void mbTimeInc(void)
{
    ModBusCom.timeOutCnt++;
}